#include "websocket_client.h"

// This message handler will be invoked once for each incoming message. It
// prints the message and then sends a copy of the message back to the server.
void on_message(WebsocketClient* c, websocketpp::connection_hdl hdl, message_ptr msg) {
	std::cout << "on_message called with hdl: " << hdl.lock().get()
		<< " and message: " << msg->get_payload()
		<< std::endl;
	websocketpp::lib::error_code ec;
	// c->send(hdl, msg->get_payload(), msg->get_opcode(), ec);
	if (ec) {
		std::cout << "Echo failed because: " << ec.message() << std::endl;
	}
    if (c->m_onread != nullptr) {
        c->m_onread(c,msg->get_payload());
    }
}


int WebsocketClient::SendMsg(const char * str,uint32_t len,
    websocketpp::frame::opcode::value opcode)
{
    if(this->m_status != WebsocketClient::CONNECTED)
        return -1;
    if(m_tls){
        websocketpp::lib::error_code ec;
        this->m_client_tls.send(m_conn_tls,str,len,opcode,ec);
        if (ec) {
            std::cout << "Echo failed because: " << ec.message() << std::endl;
            return -1;
        }
    }else{
        websocketpp::lib::error_code ec;
        this->m_client.send(m_conn,str,len,opcode,ec);
        if (ec) {
            std::cout << "Echo failed because: " << ec.message() << std::endl;
            return -1;
        }
    }
	return 0;
}

 
void on_open(WebsocketClient * c, websocketpp::connection_hdl hdl) {
    if(c->m_tls){
        TlsClient::connection_ptr con = c->m_client_tls.get_con_from_hdl(hdl);
        uint32_t usocket = con->get_raw_socket().native_handle();
        auto m_server = con->get_response_header("Server");
        std::cout<<"open from server"<<m_server<<std::endl;
        c->m_status = WebsocketClient::CONNECTED;
        if (c->m_on_connected != nullptr) {
            c->m_on_connected(c);
        }
    }else{
        Client::connection_ptr con = c->m_client.get_con_from_hdl(hdl);
        uint32_t usocket = con->get_raw_socket().native_handle();
        auto m_server = con->get_response_header("Server");
        std::cout<<"open from server"<<m_server<<std::endl;
        c->m_status = WebsocketClient::CONNECTED;
        if (c->m_on_connected != nullptr) {
            c->m_on_connected(c);
        }
    }
}


 
void on_close(WebsocketClient * c, websocketpp::connection_hdl hdl) {
    // m_status = "Open";

    // client::connection_ptr con = c->get_con_from_hdl(hdl);
    // m_server = con->get_response_header("Server");
    c->m_status = WebsocketClient::CLOSED;
    std::cout<<"on_close"<<std::endl;
    if (c->m_on_disconnected != nullptr) {
        c->m_on_disconnected(c,WebsocketClient::CloseReason::LOCAL_CLOSED);
    }
}

void on_fail(WebsocketClient * c, websocketpp::connection_hdl hdl) {
    std::cout<<"on_fail"<<std::endl;
    Client::connection_ptr con = c->m_client.get_con_from_hdl(hdl);
    auto state =  con->get_state();
    if(state == websocketpp::session::state::closed)
        std::cout<<state <<" on_fail "<<std::endl;
    c->m_status = WebsocketClient::FAIL;
    if (c->m_on_disconnected != nullptr) {
        c->m_on_disconnected(c,WebsocketClient::CloseReason::PEER_CLOSED);
    }
}

WebsocketClient::~WebsocketClient(){

    this->m_status = WebsocketClient::STOP;
    m_client.stop();
    m_thread->join();
}

WebsocketClient::WebsocketClient(std::string url,bool tls)
{
    m_tls = tls;
    m_auto_reconn = false;
	m_url = url;
    if(m_tls){
// Set logging to be pretty verbose (everything except message payloads)
        m_client_tls.set_access_channels(websocketpp::log::alevel::all);
        m_client_tls.clear_access_channels(websocketpp::log::alevel::frame_payload);
        m_client_tls.set_tls_init_handler([this](websocketpp::connection_hdl){
            return websocketpp::lib::make_shared<asio::ssl::context>(asio::ssl::context::tlsv1);
        });
        // Initialize ASIO
        m_client_tls.init_asio();
        m_thread = new std::thread([this]()
        {
            while (this->m_status != STOP)
            {
                m_client_tls.set_message_handler(bind(&on_message, this, ::_1, ::_2));
                websocketpp::lib::error_code ec;
                std::cout<<"1"<<std::endl;
                m_conn_tls = m_client_tls.get_connection(this->m_url, ec);
                m_conn_tls->set_open_handler(websocketpp::lib::bind(
                    &on_open,
                    this,
                    websocketpp::lib::placeholders::_1
                ));
                m_conn_tls->set_fail_handler(websocketpp::lib::bind(
                    &on_fail,
                    this,
                    websocketpp::lib::placeholders::_1
                ));
                m_conn_tls->set_close_handler(websocketpp::lib::bind(
                    &on_close,
                    this,
                    websocketpp::lib::placeholders::_1
                ));
                if (ec) {
                    std::cout << "could not create connection because: " << ec.message() << std::endl;
                    this->m_status = Status::FAIL;
                }
                std::cout<<"2"<<std::endl;
            
                // Note that connect here only requests a connection. No network messages are
                // exchanged until the event loop starts running in the next line.
                TlsClient::connection_ptr ptr =  m_client_tls.connect(m_conn_tls);
                if(ptr->get_state() != websocketpp::session::state::open)
                        std::cout<<ptr->get_state()  <<" websocketpp::session::state "<<std::endl;
                // Start the ASIO io_service run loop
                // this will cause a single connection to be made to the server. c.run()
                // will exit when this connection is closed.
                std::cout<<"3 " << ptr << " " <<m_conn_tls <<std::endl;
                this->m_status = WebsocketClient::CONNECTING;
                while((this->m_status != WebsocketClient::FAIL) &&
                (this->m_status != WebsocketClient::CLOSED)
                &&(this->m_status != WebsocketClient::STOP)
                ){
                    try{
                    // while(this->m_status == WebsocketClient::CONNECTED){
                        int count_of_handler = this->m_client_tls.run();
                        // std::cout<<"count_of_handler:  " << count_of_handler<<std::endl;
                    // }
                    // run应该只执行一次就会退出
                    }catch(std::exception e){
                        std::cout<< "run exception"<< e.what();
                    }
                }
                Sleep(1000);
            }
            std::cout<<"close";
            this->m_on_disconnected(this,WebsocketClient::CloseReason::LOCAL_CLOSED);
        });
    }
    else{
        // Set logging to be pretty verbose (everything except message payloads)
        m_client.set_access_channels(websocketpp::log::alevel::all);
        m_client.clear_access_channels(websocketpp::log::alevel::frame_payload);

        // Initialize ASIO
        this->m_client.init_asio();
        m_thread = new std::thread([this]()
        {
            while (this->m_status != STOP)
            {
                this->m_client.set_message_handler(bind(&on_message, this, ::_1, ::_2));
                websocketpp::lib::error_code ec;
                std::cout<<"1"<<std::endl;
                m_conn = m_client.get_connection(this->m_url, ec);
                m_conn->set_open_handler(websocketpp::lib::bind(
                    &on_open,
                    this,
                    websocketpp::lib::placeholders::_1
                ));
                m_conn->set_fail_handler(websocketpp::lib::bind(
                    &on_fail,
                    this,
                    websocketpp::lib::placeholders::_1
                ));
                m_conn->set_close_handler(websocketpp::lib::bind(
                    &on_close,
                    this,
                    websocketpp::lib::placeholders::_1
                ));
                if (ec) {
                    std::cout << "could not create connection because: " << ec.message() << std::endl;
                    this->m_status = Status::FAIL;
                }
                std::cout<<"2"<<std::endl;
            
                // Note that connect here only requests a connection. No network messages are
                // exchanged until the event loop starts running in the next line.
                Client::connection_ptr ptr =  this->m_client.connect(m_conn);
                if(ptr->get_state() != websocketpp::session::state::open)
                        std::cout<<ptr->get_state()  <<" websocketpp::session::state "<<std::endl;
                // Start the ASIO io_service run loop
                // this will cause a single connection to be made to the server. c.run()
                // will exit when this connection is closed.
                std::cout<<"3 " << ptr << " " <<m_conn_tls <<std::endl;
                this->m_status = WebsocketClient::CONNECTING;
                while((this->m_status != WebsocketClient::FAIL) &&
                (this->m_status != WebsocketClient::CLOSED)
                &&(this->m_status != WebsocketClient::STOP)
                ){
                    try{
                    // while(this->m_status == WebsocketClient::CONNECTED){
                        int count_of_handler = this->m_client.run();
                        // std::cout<<"count_of_handler:  " << count_of_handler<<std::endl;
                    // }
                    // run应该只执行一次就会退出
                    }catch(std::exception e){
                        std::cout<< "run exception"<< e.what();
                    }
                }
                Sleep(1000);
            }
            std::cout<<"close";
            this->m_on_disconnected(this,WebsocketClient::CloseReason::LOCAL_CLOSED);
        
        });
    }
    
}

int WebsocketClient::SetOnConnectedHandler(OnConnectedHandler on_connected){
    this->m_on_connected = on_connected;
    return 0;
}

int WebsocketClient::SetOnDisConnectedHandler(OnDisConnectedHandler on_disconnected){
    this->m_on_disconnected = on_disconnected;
    return 0;
}

int WebsocketClient::SetOnReadHandler(OnReadHandler onread){
    this->m_onread = onread;
    return 0;
}
